package framework.spatial;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.util.LinkedList;

/**
 *
 * @author Lukáš Marek
 */
public class FRDSpatialGPolygon extends FRDSpatialGItem
{
	LinkedList<Point> points;
	Color newColor;
	boolean add = false;
	
	public FRDSpatialGPolygon()
	{
		points = new LinkedList<>();
		Color color = new Color(defaultColor.getRed(), defaultColor.getGreen(), defaultColor.getBlue());
		newColor = color;
	}
	
	public void setLocation(LinkedList<Point> points)
	{
		for (int i = 0; i < points.size(); i++)
		{
			this.points.add(new Point(points.get(i)));
		}
	}
	
	public LinkedList<Point> getLocation()
	{
		return points;
	}
	
	@Override
	public void setPoints(Point p1, Point p2)
	{
		if (points.size() < 1) 
		{
			if (p1.distance(p2) > 3)
			{
				points.add(new Point(mapPoint(p1)));
				points.add(new Point(mapPoint(p2)));
			}
		}
		else if (add)
		{
			points.add(new Point(mapPoint(p2)));
			add = false;
		}
		else
			points.getLast().setLocation(mapPoint(p2));
		if (points.size() > 4 && points.getFirst().distance(points.getLast()) < 10)
		{
			points.getLast().setLocation(points.getFirst());
		}
	}
	
	@Override
	public boolean addPoints(Point p1, Point p2)
	{
		if (points.size() > 1)
		{
			points.getLast().setLocation(mapPoint(p2));
			if (points.getFirst().distance(points.getLast()) < 10)
			{
				points.getLast().setLocation(points.getFirst());
				return true;
			}
			else
			{
				add = true;
			}
		}
		return false;
	}

	@Override
	public void draw(Graphics g)
	{
		if (selected)
			g.setColor(SelectedColor);
		else
			g.setColor(defaultColor);

		if (points.size() > 3 && points.getFirst().distance(points.getLast()) == 0)
		{
			int x[] = new int [points.size()];
			int y[] = new int [points.size()];
			for (int i=0; i < points.size(); i++)
			{
				Point p = showPoint(points.get(i));
				x[i] = p.x;
				y[i] = p.y;
			}
			g.fillPolygon(x, y, points.size());
		}
		else
		{
			g.setColor(newColor);
			for (int i=0; i < (points.size() - 1); i++)
			{
				Point r1 = showPoint(points.get(i));
				Point r2 = showPoint(points.get(i + 1));
				g.drawLine(r1.x, r1.y, r2.x, r2.y);
			}
		}
	}

	@Override
	public void drawEdit(Graphics g, Point pt1, Point pt2)
	{
		g.setColor(editColor);
		Point p;
		int c = -1;
		Point diff = new Point(0,0);
		for (int i=0; i < points.size()-1; i++)
		{
			if (near(points.get(i),mapPoint(pt1)))
			{
				c = i;
				break;
			}
		}
		if (c < 0 && undermouse(pt1))
		{
			diff.setLocation(pt2.x - pt1.x,pt2.y - pt1.y);
		}
		int x[] = new int [points.size()];
		int y[] = new int [points.size()];
		for (int i=0; i < points.size(); i++)
		{
			if (i == c)
				p = pt2;
			else
				p = showPoint(points.get(i));
			x[i] = p.x + diff.x;
			y[i] = p.y + diff.y;
		}
		if (c == 0)
		{
			x[points.size()-1] = pt2.x + diff.x;
			y[points.size()-1] = pt2.y + diff.y;
		}
		g.fillPolygon(x, y, points.size());
	}
	
	@Override
	public void saveEdit(Point pt1, Point pt2)
	{
		int c = -1;
		for (int i=0; i < points.size()-1; i++)
		{
			if (near(points.get(i),mapPoint(pt1)))
			{
				c = i;
				break;
			}
		}
		if (c > 0)
		{
			points.get(c).setLocation(mapPoint(pt2));
		}
		else if (c == 0)
		{
			points.getFirst().setLocation(mapPoint(pt2));
			points.getLast().setLocation(points.getFirst());
		}
		else if (undermouse(pt1))
		{
			Point mp1 = mapPoint(pt1);
			Point mp2 = mapPoint(pt2);
			for (int i=0; i < points.size(); i++)
			{
				points.get(i).x += mp2.x - mp1.x;
				points.get(i).y += mp2.y - mp1.y;
			}
		}
	}
	
	/**
	 * Zjisteni blizkosti dvou bodu
	 * @param _p prvni bod
	 * @param p druhy bod
	 * @return true pokud sou body blizko
	 */
	protected boolean near(Point _p, Point p)
	{
		double zoom = back.getZoom();
		return (p.distance(_p) < (10*4/zoom));
	}

	@Override
	public boolean undermouse(Point _p)
	{
		Point size = null;
		if (back instanceof FRDSpatialMapImageBackground) size = ((FRDSpatialMapImageBackground)back).mapSize();
		else throw new UnsupportedOperationException("Nelze poutivat tridu FRDSpatialGPolygon pokud pro zobrazovani pozadi neni pouzita trida FRDSpatialMapImageBackground");
		if (size != null)
		{
			BufferedImage img = new BufferedImage(size.x, size.y, BufferedImage.TYPE_INT_ARGB);
			Graphics buf = img.getGraphics();
			fill(buf);
			Point p = mapPoint(_p);
			return (img.getRGB(p.x, p.y) == Color.white.getRGB());
		}
		else
			return false;
	}
	
	/**
	 * Vyplneni polygonu pro test na bod uvnitr objektu
	 * @param _g grafika
	 */
	void fill(Graphics _g)
	{
		int x[] = new int [points.size()];
		int y[] = new int [points.size()];
		for (int i=0; i < points.size(); i++)
		{
			Point p = points.get(i);
			x[i] = p.x;
			y[i] = p.y;
		}
		_g.fillPolygon(x, y, points.size());
	}
	
	/**
	 * prepocita bod z mapy na bod v panelu
	 * @param pt bod
	 * @return 
	 */
	private Point showPoint(Point pt)
	{
		double scale = back.getScale();
		Point zoomLoc = back.getPosition();
		double zoom = back.getZoom();
		double df = scale / zoom;
		Point ret = new Point();
		ret.x = (int)((pt.x - zoomLoc.x) / df);
		ret.y = (int)((pt.y - zoomLoc.y) / df);
		return ret;
	}
	
	/**
	 * prepocita bod z panelu na bod v mape
	 * @param pt
	 * @return 
	 */
	private Point mapPoint(Point pt)
	{
		double scale = back.getScale();
		Point zoomLoc = back.getPosition();
		double zoom = back.getZoom();
		double df = scale / zoom;
		Point ret = new Point();
		ret.x = (int)(pt.x * df) + zoomLoc.x;
		ret.y = (int)(pt.y * df) + zoomLoc.y;
		return ret;
	}
}
